package com.evan.review.thread;

import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import androidx.appcompat.app.AppCompatActivity;

import com.evan.review.R;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * Activity demonstrating thread safety issues and solutions
 */
public class ThreadSafetyDemoActivity extends AppCompatActivity {
    
    private TextView tvStatus;
    private TextView tvResult;
    
    // Regular counter (not thread-safe)
    private int unsafeCounter = 0;
    
    // Synchronized method counter
    private int syncMethodCounter = 0;
    
    // Synchronized block counter
    private int syncBlockCounter = 0;
    
    // Volatile flag for visibility demonstration
    private volatile boolean stopFlag = false;
    
    // AtomicInteger counter (thread-safe)
    private AtomicInteger atomicCounter = new AtomicInteger(0);
    
    // Lock examples
    private final Lock lock = new ReentrantLock();
    private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    private int lockCounter = 0;
    
    // Thread-unsafe and safe collections
    private List<Integer> unsafeList = new ArrayList<>();
    private List<Integer> safeList = Collections.synchronizedList(new ArrayList<>());
    private List<Integer> copyOnWriteList = new CopyOnWriteArrayList<>();
    private ConcurrentHashMap<Integer, String> concurrentMap = new ConcurrentHashMap<>();
    
    // Executor for running tasks
    private ExecutorService executor;
    
    // Handler for updating UI
    private final Handler mainHandler = new Handler(Looper.getMainLooper());
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_thread_safety_demo);
        
        tvStatus = findViewById(R.id.tv_status);
        tvResult = findViewById(R.id.tv_result);
        
        // Create a fixed thread pool for our tests
        executor = Executors.newFixedThreadPool(10);
        
        setupButtons();
    }
    
    private void setupButtons() {
        // Test unsafe counter
        Button btnUnsafeCounter = findViewById(R.id.btn_unsafe_counter);
        btnUnsafeCounter.setOnClickListener(v -> testUnsafeCounter());
        
        // Test synchronized method
        Button btnSyncMethod = findViewById(R.id.btn_sync_method);
        btnSyncMethod.setOnClickListener(v -> testSynchronizedMethod());
        
        // Test synchronized block
        Button btnSyncBlock = findViewById(R.id.btn_sync_block);
        btnSyncBlock.setOnClickListener(v -> testSynchronizedBlock());
        
        // Test volatile
        Button btnVolatile = findViewById(R.id.btn_volatile);
        btnVolatile.setOnClickListener(v -> testVolatile());
        
        // Test atomic
        Button btnAtomic = findViewById(R.id.btn_atomic);
        btnAtomic.setOnClickListener(v -> testAtomicCounter());
        
        // Test locks
        Button btnLocks = findViewById(R.id.btn_locks);
        btnLocks.setOnClickListener(v -> testLocks());
        
        // Test thread-safe collections
        Button btnCollections = findViewById(R.id.btn_collections);
        btnCollections.setOnClickListener(v -> testCollections());
        
        // Test deadlock
        Button btnDeadlock = findViewById(R.id.btn_deadlock);
        btnDeadlock.setOnClickListener(v -> createDeadlock());
        
        // Reset all
        Button btnReset = findViewById(R.id.btn_reset);
        btnReset.setOnClickListener(v -> resetAll());
    }
    
    // 1. Demonstrate thread-unsafe counter
    private void testUnsafeCounter() {
        tvStatus.setText("Testing Unsafe Counter");
        unsafeCounter = 0;
        
        for (int i = 0; i < 1000; i++) {
            executor.submit(() -> {
                // Race condition happens here
                unsafeCounter++;
            });
        }
        
        // Check result after all tasks should be done
        mainHandler.postDelayed(() -> {
            tvResult.setText("Unsafe Counter (Expected: 1000): " + unsafeCounter);
        }, 500);
    }
    
    // 2. Demonstrate synchronized method
    private void testSynchronizedMethod() {
        tvStatus.setText("Testing Synchronized Method");
        syncMethodCounter = 0;
        
        for (int i = 0; i < 1000; i++) {
            executor.submit(this::incrementSyncMethodCounter);
        }
        
        mainHandler.postDelayed(() -> {
            tvResult.setText("Synchronized Method (Expected: 1000): " + syncMethodCounter);
        }, 500);
    }
    
    // Synchronized method
    private synchronized void incrementSyncMethodCounter() {
        syncMethodCounter++;
    }
    
    // 3. Demonstrate synchronized block
    private void testSynchronizedBlock() {
        tvStatus.setText("Testing Synchronized Block");
        syncBlockCounter = 0;
        
        for (int i = 0; i < 1000; i++) {
            executor.submit(() -> {
                // Synchronized block only locks on specific object (this)
                synchronized (this) {
                    syncBlockCounter++;
                }
            });
        }
        
        mainHandler.postDelayed(() -> {
            tvResult.setText("Synchronized Block (Expected: 1000): " + syncBlockCounter);
        }, 500);
    }
    
    // 4. Demonstrate volatile
    private void testVolatile() {
        tvStatus.setText("Testing Volatile (Check Logs)");
        stopFlag = false;
        
        // Background thread that watches the volatile flag
        Thread watcher = new Thread(() -> {
            int count = 0;
            while (!stopFlag) {
                count++;
                // Busy loop - this would never exit without
                // volatile on stopFlag because JVM might optimize
                // and never re-read stopFlag from main memory
            }
            final int finalCount = count;
            mainHandler.post(() -> {
                tvResult.setText("Volatile: Watcher counted to " + finalCount + " before stopping");
            });
        });
        watcher.start();
        
        // Set the flag after a delay
        mainHandler.postDelayed(() -> {
            stopFlag = true;
            tvStatus.setText("Set stopFlag = true");
        }, 1000);
    }
    
    // 5. Demonstrate AtomicInteger
    private void testAtomicCounter() {
        tvStatus.setText("Testing AtomicInteger");
        atomicCounter.set(0);
        
        for (int i = 0; i < 1000; i++) {
            executor.submit(() -> {
                // Thread-safe operation
                atomicCounter.incrementAndGet();
            });
        }
        
        mainHandler.postDelayed(() -> {
            tvResult.setText("AtomicInteger (Expected: 1000): " + atomicCounter.get());
        }, 500);
    }
    
    // 6. Demonstrate explicit locks
    private void testLocks() {
        tvStatus.setText("Testing Locks");
        lockCounter = 0;
        
        // ReentrantLock example
        for (int i = 0; i < 500; i++) {
            executor.submit(() -> {
                lock.lock();
                try {
                    lockCounter++;
                } finally {
                    lock.unlock();
                }
            });
        }
        
        // ReadWriteLock example (write lock)
        for (int i = 0; i < 500; i++) {
            executor.submit(() -> {
                readWriteLock.writeLock().lock();
                try {
                    lockCounter++;
                } finally {
                    readWriteLock.writeLock().unlock();
                }
            });
        }
        
        mainHandler.postDelayed(() -> {
            tvResult.setText("Lock-protected counter (Expected: 1000): " + lockCounter);
        }, 500);
    }
    
    // 7. Demonstrate thread-safe collections
    private void testCollections() {
        tvStatus.setText("Testing Thread-Safe Collections");
        
        unsafeList.clear();
        safeList.clear();
        copyOnWriteList.clear();
        concurrentMap.clear();
        
        // Test ArrayList (unsafe)
        for (int i = 0; i < 1000; i++) {
            final int value = i;
            executor.submit(() -> {
                try {
                    unsafeList.add(value);
                } catch (Exception e) {
                    // May throw ConcurrentModificationException
                }
            });
        }
        
        // Test synchronized List
        for (int i = 0; i < 1000; i++) {
            final int value = i;
            executor.submit(() -> safeList.add(value));
        }
        
        // Test CopyOnWriteArrayList
        for (int i = 0; i < 1000; i++) {
            final int value = i;
            executor.submit(() -> copyOnWriteList.add(value));
        }
        
        // Test ConcurrentHashMap
        for (int i = 0; i < 1000; i++) {
            final int key = i;
            executor.submit(() -> concurrentMap.put(key, "Value" + key));
        }
        
        mainHandler.postDelayed(() -> {
            StringBuilder result = new StringBuilder();
            result.append("ArrayList size (may have errors): ").append(unsafeList.size()).append("\n");
            result.append("Synchronized List size: ").append(safeList.size()).append("\n");
            result.append("CopyOnWriteArrayList size: ").append(copyOnWriteList.size()).append("\n");
            result.append("ConcurrentHashMap size: ").append(concurrentMap.size());
            
            tvResult.setText(result.toString());
        }, 1000);
    }
    
    // 8. Demonstrate deadlock (be careful with this!)
    private final Object lock1 = new Object();
    private final Object lock2 = new Object();
    
    private void createDeadlock() {
        tvStatus.setText("Attempting to create a Deadlock (UI may freeze)");
        
        // First thread - acquires lock1, then tries to acquire lock2
        executor.submit(() -> {
            synchronized (lock1) {
                tvStatus.post(() -> tvStatus.setText("Thread 1: Acquired lock1, waiting for lock2..."));
                
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
                
                synchronized (lock2) {
                    tvStatus.post(() -> tvStatus.setText("Thread 1: Acquired both locks (deadlock avoided)"));
                }
            }
        });
        
        // Second thread - acquires lock2, then tries to acquire lock1
        executor.submit(() -> {
            synchronized (lock2) {
                tvStatus.post(() -> tvStatus.setText("Thread 2: Acquired lock2, waiting for lock1..."));
                
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
                
                synchronized (lock1) {
                    tvStatus.post(() -> tvStatus.setText("Thread 2: Acquired both locks (deadlock avoided)"));
                }
            }
        });
        
        // Set a timeout to warn about deadlock
        mainHandler.postDelayed(() -> {
            tvResult.setText("Potential deadlock detected. If UI is responsive, " +
                    "deadlock was avoided by JVM scheduling. Try again to increase chance of deadlock.");
        }, 2000);
    }
    
    // Reset all counters
    private void resetAll() {
        unsafeCounter = 0;
        syncMethodCounter = 0;
        syncBlockCounter = 0;
        stopFlag = false;
        atomicCounter.set(0);
        lockCounter = 0;
        
        unsafeList.clear();
        safeList.clear();
        copyOnWriteList.clear();
        concurrentMap.clear();
        
        tvStatus.setText("All variables reset");
        tvResult.setText("");
    }
    
    @Override
    protected void onDestroy() {
        super.onDestroy();
        
        // Proper cleanup
        executor.shutdownNow();
    }
} 